package com.smedi.ismedi.personnel.core.application.service.impl;

import com.smedi.ismedi.cache.starter.util.SafetyLockUtil;
import com.smedi.ismedi.personnel.api.dto.response.PersonSyncFromImisResponse;
import com.smedi.ismedi.personnel.api.dto.response.SubOrganizationSyncFromImisResponse;
import com.smedi.ismedi.personnel.core.application.service.ImisSyncApplicationService;
import com.smedi.ismedi.personnel.core.application.service.PersonApplicationService;
import com.smedi.ismedi.personnel.core.application.service.ProfileImageApplicationService;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.Person;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.PersonSyncFromImis;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.SubOrganizationSyncFromImis;
import com.smedi.ismedi.personnel.core.domain.repository.PersonSyncFromImisRepository;
import com.smedi.ismedi.personnel.core.domain.service.PersonSyncFromImisDomainService;
import com.smedi.ismedi.personnel.core.domain.service.SubOrganizationSyncFromImisDomainService;
import com.smedi.ismedi.personnel.core.infrastructure.constant.ConstantField;
import com.smedi.ismedi.personnel.core.infrastructure.constant.CustomError;
import com.smedi.ismedi.personnel.core.port.adapter.service.ImisRemoteApiService;
import com.smedi.ismedi.personnel.core.port.adapter.service.OrganizationRemoteApiService;
import com.smedi.ismedi.web.starter.exception.LogicException;
import com.smedi.ismedi.web.starter.util.DozerUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * @Author: xulinjun
 * @Date: 2021-6-16 11:38
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ImisSyncApplicationServiceImpl implements ImisSyncApplicationService {
    private final DozerUtil dozerUtil;

    private final ImisRemoteApiService imisRemoteApiService;
    private final SubOrganizationSyncFromImisDomainService subOrganizationSyncFromImisDomainService;
    private final PersonSyncFromImisDomainService personSyncFromImisDomainService;
    private final PersonSyncFromImisRepository personSyncFromImisRepository;
    private final OrganizationRemoteApiService organizationRemoteApiService;
    private final PersonApplicationService personApplicationService;
    private final ProfileImageApplicationService profileImageApplicationService;

    private final SafetyLockUtil safetyLockUtil;

    @Value("${spring.application.name}")
    private String application;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void doSync() {
        String lockKey = ConstantField.doSyncTask(application);
        log.info(">>> 开始同步数据。");

        // 执行加锁代码
        safetyLockUtil.calc(() -> {
            String imisToken = imisRemoteApiService.callToken(); // 获取用户令牌

            // 同步组织信息
            log.info("开始同步二级组织 >>> {}", new Date());
            List<SubOrganizationSyncFromImisResponse> subOrganizationList = imisRemoteApiService.callSubOrganizationSync(imisToken);
            List<SubOrganizationSyncFromImis> subOrganizationSyncFromImis = dozerUtil.convertor(subOrganizationList, SubOrganizationSyncFromImis.class);
            log.info("同步二级组织结束 >>> {}", new Date());

            log.info("开始同步人员信息 >>> {}", new Date());
            List<PersonSyncFromImisResponse> personList = imisRemoteApiService.callPersonSync(imisToken);
            List<PersonSyncFromImis> personSyncFromImis = dozerUtil.convertor(personList, PersonSyncFromImis.class);
            log.info("同步人员信息结束 >>> {}", new Date());

            // 删除历史数据
            subOrganizationSyncFromImisDomainService.deleteAll();
            personSyncFromImisDomainService.deleteAll();
            log.info("历史数据已清除 >>> {}", new Date());

            // 保存新数据
            subOrganizationSyncFromImisDomainService.saveAll(subOrganizationSyncFromImis);
            personSyncFromImisDomainService.saveAll(personSyncFromImis);
            log.info("新数据已保存 >>> {}", new Date());
        }, lockKey, 5, 120, 1, 3, false);
    }

    @Override
    public List<PersonSyncFromImis> batchSavePerson(Set<Long> referencePersonIds, Long hrOrganizationId) {
        if (referencePersonIds.size() > 20) {
            throw LogicException.raise(CustomError.BATCH_SAVE_MAX_ERROR);
        } else {
            // 判断当前行政组织是否存在
            boolean available = organizationRemoteApiService.checkHrOrganizationAvailable(hrOrganizationId);

            if (!available) {
                throw LogicException.raise(CustomError.ORG_NOT_AVAILABLE);
            }
            // 根据id获取到中间表的信息
            List<PersonSyncFromImis> list = new ArrayList<>();
            for (Long referencePersonId : referencePersonIds) {
                // 判断是否存在
                PersonSyncFromImis personSyncFromImis = personSyncFromImisRepository.queryByReferencePersonId(referencePersonId);
                if (personSyncFromImis != null) {
                    list.add(personSyncFromImis);
                } else {
                    throw LogicException.raise(CustomError.PERSON_ID_NOT_EXIST);
                }
            }

            // 处理实体保存数据
            Map<Long, Long> succeedMap = new HashMap<>();
            for (PersonSyncFromImis personSyncFromImis : list) {
                try {
                    Person person = personApplicationService.savePersonInfoWithOutImage(personSyncFromImis, hrOrganizationId);
                    succeedMap.put(personSyncFromImis.getReferencePersonId(), person.getPersonId());
                    personSyncFromImis.setSaved(true);
                } catch (Exception e) {
                    personSyncFromImis.setSaved(false);
                    personSyncFromImis.setErrorMessage(e.getMessage());
                    e.printStackTrace();
                }
            }

            // 异步处理照片信息
            if (succeedMap.size() > 0) {
                profileImageApplicationService.copyProfileImageByRemoteApi(succeedMap);
            }

            return list;
        }
    }

    @Override
    public List<PersonSyncFromImis> batchSavePersonToProjectOrganization(Set<Long> referencePersonIds, Long projectId, Long projectOrganizationId) {
        if (referencePersonIds.size() > 20) {
            throw LogicException.raise(CustomError.BATCH_SAVE_MAX_ERROR);
        } else {
            // 判断当前项目组织是否存在
            boolean available = organizationRemoteApiService.checkProjectOrganizationAvailable(projectOrganizationId);

            if (!available) {
                throw LogicException.raise(CustomError.ORG_NOT_AVAILABLE);
            }
            // 根据id获取到中间表的信息
            List<PersonSyncFromImis> list = new ArrayList<>();
            for (Long referencePersonId : referencePersonIds) {
                // 判断是否存在
                PersonSyncFromImis personSyncFromImis = personSyncFromImisRepository.queryByReferencePersonId(referencePersonId);
                if (personSyncFromImis != null) {
                    list.add(personSyncFromImis);
                } else {
                    throw LogicException.raise(CustomError.PERSON_ID_NOT_EXIST);
                }
            }

            // 处理实体保存数据
            Map<Long, Long> succeedMap = new HashMap<>();
            for (PersonSyncFromImis personSyncFromImis : list) {
                try {
                    Person person = personApplicationService.saveProjectPersonInfoWithOutImage(personSyncFromImis, projectId, projectOrganizationId);
                    succeedMap.put(personSyncFromImis.getReferencePersonId(), person.getPersonId());
                    personSyncFromImis.setSaved(true);
                } catch (Exception e) {
                    personSyncFromImis.setSaved(false);
                    personSyncFromImis.setErrorMessage(e.getMessage());
                    e.printStackTrace();
                }
            }

            // 异步处理照片信息
            if (succeedMap.size() > 0) {
                profileImageApplicationService.copyProfileImageByRemoteApi(succeedMap);
            }

            return list;
        }
    }
}
